﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using RaytracerLib.RenderableObjects;
using RaytracerLib.Rays;
using System.Diagnostics;

namespace RaytracerLib.SpatialSubdivisioners
{
    public class NonSpatialSubdivisioner : SpatialSubdivisioner
    {
        public NonSpatialSubdivisioner(Scene a_scene)
            : base(a_scene)
        {
        }

        public override void BuildSpatialSubdivision()
        {
        }

        public override RenderableObject FindOuterObject(Ray a_ray)
        {
            double dist = Constants.DOUBLE_MAX;
            Intersection nearest = null;

            m_scene.Statistics.FindOuterObject++;

            foreach (var obj in m_scene.RenderableObjects)
            {
                if (!obj.Closed)
                    continue;

                m_scene.Statistics.IntersectionTested++;

                Intersection test = obj.GetIntersection(null, a_ray);

                if (!test.Exists)
                    continue;

                if (!test.BackHit)
                    continue;

                if (test.Dist < dist)
                {
                    dist = test.Dist;
                    nearest = test;
                }
            }

            if (nearest != null)
                return nearest.SceneObject;
            else
                return null;
        }

        public override Intersection HitTest(Intersection a_source_ray_intersection, Ray a_ray)
        {
            if (a_ray.Depth >= m_max_ray_depth)
            {
                m_scene.Statistics.RaysToDepth++;
                return m_scene.NoIntersection;
            }

            Intersection res = m_scene.NoIntersection;

            foreach (RenderableObject sp in m_scene.RenderableObjects)
            {
                m_scene.Statistics.IntersectionTested++;

                Intersection i = sp.GetIntersection(a_source_ray_intersection, a_ray);

                if (res.Dist <= i.Dist)
                    continue;

                // In case of two shapes side by side.
                if (i.Dist.IsAlmostRelativeLessOrEqualThen(0))
                    continue;

                res = i;
            }

            return res;
        }

        public override RenderableObject FindOuterObject(Intersection a_intersection)
        {
            double dist = Constants.DOUBLE_MAX;
            Intersection nearest = null;

            m_scene.Statistics.FindOuterObject++;

            foreach (var obj in m_scene.RenderableObjects)
            {
                if (!obj.Closed)
                    continue;

                m_scene.Statistics.IntersectionTested++;

                Intersection test = obj.GetIntersection(a_intersection,
                    a_intersection.BackHit ? 
                    new ShadowRay(a_intersection, a_intersection.SourceRay.Dir, RaySurfaceSide.RefractedSide) : 
                    a_intersection.ReflectedRay);

                if (!test.Exists)
                    continue;

                if (!test.BackHit)
                    continue;

                if (test.Dist < dist)
                {
                    dist = test.Dist;
                    nearest = test;
                }
            }

            if (nearest != null)
                return nearest.SceneObject;
            else
                return null;
        }
    }
}
